﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.IO;
using System.Xml;

namespace INNOVACALL.Framework.Common
{
    public class XMLFactory
    {

        public XDocument CurrentDocument { get; set; }

        public XMLFactory() { }

        public XMLFactory(string xmlDocumentString)
        {
            this.CurrentDocument = XDocument.Load(new StringReader(xmlDocumentString));
        }


        #region Properties
        public enum ACTION { COPY = 1, MOVE };
        public enum POSITION { BEFORE = 1, AFTER, FIRST, LAST };
        #endregion

        #region Public Methods

        public string SaveToString()
        {
            string toReturn = string.Empty;
            XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
            xmlWriterSettings.Encoding = new UTF8Encoding();
            using (var stream = new MemoryStream())
            {
                using (XmlWriter writer = XmlWriter.Create(stream, xmlWriterSettings))
                {
                    this.CurrentDocument.Save(writer);
                }
                toReturn = Encoding.UTF8.GetString(stream.ToArray());
            }
            return toReturn;
        }

        /// <summary>
        /// Déplace ou copie le noeud sélectionné vers un emplacement sélectionné
        /// </summary>
        /// <param name="xDocumentSource"></param>
        /// <param name="xPathSource"></param>
        /// <param name="xDocumentCible"></param>
        /// <param name="xPathCible"></param>
        /// <param name="action">Copie ou Déplace</param>
        public void MoveNode(XDocument xDocumentSource, String xPathSource, XDocument xDocumentCible, String xPathCible, int action)
        {
            XElement xElementSource = xDocumentSource.XPathSelectElement(xPathSource);
            XElement xElementCible = xDocumentCible.XPathSelectElement(xPathCible);
            if (xElementSource != null && xElementCible != null)
            {
                switch (action)
                {
                    case 1:
                        xElementCible.AddAfterSelf(xElementSource);
                        break;
                    case 2:
                        xElementCible.AddAfterSelf(xElementSource);
                        xElementSource.Remove();
                        break;
                    default:
                        throw new Exception("Action incorrecte");
                }
            }
            else
            {
                throw new Exception("XPath incorrecte");
            }
        }

        /// <summary>
        /// Supprime le noeud sélectionné
        /// </summary>
        /// <param name="xDocumentSource"></param>
        /// <param name="xPathSource"></param>
        /// <param name="RecurseChildren">Si false, alors le noeud sélectionné ne doit pas avoir d'enfants pour être supprimé</param>
        public void RemoveNode(XDocument xDocumentSource, String xPathSource, bool RecurseChildren)
        {
            XElement xElementSource = xDocumentSource.XPathSelectElement(xPathSource);
            if (xElementSource != null)
            {
                if (xElementSource.HasElements && !RecurseChildren)
                {
                    throw new Exception("Ce noeud possède des enfants");
                }
                xElementSource.Remove();
            }
        }

        /// <summary>
        /// Remplace le noeud sélectionné par xElement
        /// </summary>
        /// <param name="xElement"></param>
        /// <param name="xDocumentCible"></param>
        /// <param name="xPathCible"></param>
        public void ReplaceNode(XElement xElement, XDocument xDocumentCible, String xPathCible)
        {
            XElement xElementCible = xDocumentCible.XPathSelectElement(xPathCible);
            if (xElementCible != null)
            {
                xElementCible.Parent.AddFirst(xElement);
                xElementCible.Remove();
            }
            else
            {
                throw new Exception("XPath incorrecte");
            }
        }

        /// <summary>
        /// Insère xElement à l'emplacement sélectionné
        /// </summary>
        /// <param name="xElement"></param>
        /// <param name="xDocumentCible"></param>
        /// <param name="xPathCible"></param>
        /// <param name="Position">AFTER - BEFORE - FIRSTCHILD - LASTCHILD</param>
        public void InsertNode(XElement xElement, XDocument xDocumentCible, String xPathCible, int Position)
        {
            XElement xElementCible = xDocumentCible.XPathSelectElement(xPathCible);
            if (xElementCible != null)
            {
                switch (Position)
                {
                    case 1:
                        xElementCible.AddAfterSelf(xElement);
                        break;
                    case 2:
                        xElementCible.AddBeforeSelf(xElement);
                        break;
                    case 3:
                        xElementCible.AddFirst(xElement);
                        break;
                    case 4:
                        xElementCible.Add(xElement);
                        break;
                    default:
                        throw new Exception("Position invalide");
                }
            }
            else
            {
                throw new Exception("XPath incorrecte");
            }
        }

        /// <summary>
        /// Ajoute l'attribut xAttribute à xElement
        /// </summary>
        /// <param name="xElement"></param>
        /// <param name="xAttribute"></param>
        public void AddAttribute(XElement xElement, XAttribute xAttribute)
        {
            xElement.Add(xAttribute);
        }

        /// <summary>
        /// Supprime l'attribut xAttribute de xElement
        /// </summary>
        /// <param name="xElement"></param>
        /// <param name="xAttribute"></param>
        public void RemoveAttribute(XElement xElement, XAttribute xAttribute)
        {
            xElement.Attributes(xAttribute.Name).Remove();
        }

        /// <summary>
        /// Définit la valeur de l'attribut xAttribute
        /// </summary>
        /// <param name="xAttribute"></param>
        /// <param name="value"></param>
        public void SetAttributeValue(XAttribute xAttribute, string value)
        {
            xAttribute.SetValue(value);
        }

        /// <summary>
        /// Efface la valeur de l'attribut xAttribute
        /// </summary>
        /// <param name="xAttribute"></param>
        public void ClearAttributeValue(XAttribute xAttribute)
        {
            xAttribute.SetValue(null);
        }

        /// <summary>
        /// Efface la valeur du noeud xElement
        /// </summary>
        /// <param name="xElement"></param>
        public void ClearNodeContent(XElement xElement)
        {
            xElement.SetValue(null);
        }

        /// <summary>
        /// Définit la valeur du noeud xElement
        /// </summary>
        /// <param name="xElement"></param>
        /// <param name="value"></param>
        public void SetNodeContent(XElement xElement, string value)
        {
            xElement.SetValue(value);
        }

        /// <summary>
        /// Remplace toutes les occurences d'une valeur par une autre valeur
        /// </summary>
        /// <param name="xElement"></param>
        /// <param name="valueToBeReplaced"></param>
        /// <param name="valueWhichReplace"></param>
        public void ReplaceInContent(XElement xElement, string valueToBeReplaced, string valueWhichReplace)
        {
            xElement.SetValue(xElement.Value.Replace(valueToBeReplaced, valueWhichReplace));
        }
        #endregion
    }
}
